package com.gun3y.extractor;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.htmlparser.Node;
import org.htmlparser.NodeFilter;
import org.htmlparser.Parser;
import org.htmlparser.filters.AndFilter;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.gun3y.entity.Wrapper;
import com.gun3y.entity.pattern.FieldPattern;
import com.gun3y.entity.pattern.PositionPattern;
import com.gun3y.entity.pattern.TagType;
import com.gun3y.entity.result.ResultField;
import com.gun3y.exception.ExceptionCode;
import com.gun3y.exception.SWException;
import com.gun3y.factory.ParserFactory;
import com.gun3y.utils.Commons;

@Service
public class HtmlExtractor {

    @Autowired
    private ParserFactory parserFactory;

    public List<ResultField> extract(Wrapper wrapper, String html) {
	if (wrapper == null || !wrapper.validate() || StringUtils.isBlank(html)) {
	    throw new SWException(ExceptionCode.InvalidInput);
	}
	List<FieldPattern> fieldPatternList = wrapper.getFields();

	return extract(fieldPatternList, html, wrapper.getBase(), wrapper.getEncoding());
    }

    private List<ResultField> extract(List<FieldPattern> fieldPatternList, String html, String coverUrl, String encoding) {

	List<ResultField> fields = new ArrayList<ResultField>();

	for (FieldPattern fieldPattern : fieldPatternList) {
	    Node[] nodeList = extractNodes(html, fieldPattern.getPosition(), encoding);
	    List<ResultField> retVal = new ArrayList<ResultField>();

	    for (Node node : nodeList) {
		retVal.addAll(fieldPattern.getFieldType().extractField(node, fieldPattern, coverUrl));
	    }

	    if (retVal.isEmpty()) {
		continue;
	    }

	    if (fieldPattern.isMultipleResults()) {
		fields.addAll(retVal);
	    }
	    else {
		fields.add(retVal.get(0));
	    }

	}
	return fields;
    }

    private Node[] extractNodes(String html, PositionPattern positionPattern, String encoding) {

	String enc = encoding;
	if (StringUtils.isBlank(enc)) {
	    enc = Commons.DEFAULT_ENCODING;
	}

	Parser parser = parserFactory.createParser(html, enc);
	NodeFilter envelopFilter = getNodeFilterFromPositionEntity(positionPattern);

	try {
	    NodeList nodeList = parser.extractAllNodesThatMatch(envelopFilter);

	    if (nodeList == null || nodeList.size() == 0) {
		return new Node[0];
	    }

	    return nodeList.toNodeArray();
	}
	catch (ParserException e) {
	    throw new SWException(ExceptionCode.UnknownError);
	}
    }

    private NodeFilter getNodeFilterFromPositionEntity(PositionPattern position) {
	AndFilter andFilter = new AndFilter();
	if (position == null || position.getLocalPattern() == null) {
	    return andFilter;
	}
	List<NodeFilter> envelopfilterlist = new ArrayList<NodeFilter>();

	envelopfilterlist.add(TagType.LOCAL_TAG.getNodeFilter(position.getLocalPattern()));

	if (position.getParentPattern() != null) {
	    envelopfilterlist.add(TagType.PARENT_TAG.getNodeFilter(position.getParentPattern()));
	}

	if (position.getPreSiblingPattern() != null) {
	    envelopfilterlist.add(TagType.PREVIOUS_SIBLING_TAG.getNodeFilter(position.getPreSiblingPattern()));
	}

	if (position.getNextSiblingPattern() != null) {
	    envelopfilterlist.add(TagType.NEXT_SIBLING_TAG.getNodeFilter(position.getNextSiblingPattern()));
	}

	if (position.getChildPattern() != null) {
	    envelopfilterlist.add(TagType.CHILD_TAG.getNodeFilter(position.getChildPattern()));
	}

	andFilter.setPredicates(envelopfilterlist.toArray(new NodeFilter[envelopfilterlist.size()]));
	return andFilter;
    }

}
